package org.springblade.modules.opcua.action;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
import org.springblade.common.utils.FuncUtil;
import org.springblade.common.utils.PingKit;
import org.springblade.modules.opcua.bo.ShardingBO;
import org.springblade.modules.opcua.kit.WorkCache;
import org.springblade.modules.opcua.kit.WorkUtil;
import org.springblade.modules.opcua.model.OpcTargetBean;
import org.springblade.modules.opcua.kit.OpcUaUtil;

import java.util.*;

@Slf4j
public class OpcJhWorker implements Runnable {

    private OpcTargetBean opcTargetBean;

    private OpcDataSaveFactory factory;

    //缓存的Id列表
    private Set<String> opcIdList = new HashSet<>();


    /**
     * 初始化
     *
     * @param inBean
     */
    public OpcJhWorker(OpcTargetBean inBean, OpcDataSaveFactory inFactory) {
        this.opcTargetBean = inBean;
        this.factory = inFactory;
    }

    //最大连接数
    public final static Integer MAX_CONN = 1;

    //上次查询key的时间
    public Long lastTime = System.currentTimeMillis();

    @Override
    public void run() {
        //测试网络是否通
        if (!PingKit.isOk(opcTargetBean.getOpcIp())) {
            log.error("\r\n******服务器PING 不通{}，跳过*********", opcTargetBean.getOpcIp());
            return;
        }
        //分配
        ShardingBO bo = ShardingManager.getSharding();
        if (bo.getWorkerSize() <= 0 || opcTargetBean.getId() % bo.getWorkerSize() != bo.getWorkerIdx()) {
            return;
        }
        try {
            //业务操作
            action();
        } catch (Exception ex) {
            ex.printStackTrace();
            log.error(ex.getMessage(), ex);
        } finally {

        }
    }


    /**
     * 业务操作
     */
    private void action() {
        try {
            Long start = System.currentTimeMillis();
            //取得所有OPC,key
            Set<String> keySet = getOpcIdList();
            List<String> keys = new ArrayList<>(keySet);
            log.info("读取所有耗时：{}\r\n{}", (System.currentTimeMillis() - start), FuncUtil.join(keys));
            start = System.currentTimeMillis();
            //取得数据
            Map<String, Object> dataMap = getOpcValue(keys);
            //缓存
            log.info("读取所有值个数：{}，耗时：{}", dataMap.size(), (System.currentTimeMillis() - start));
            WorkUtil.submit(factory.create(opcTargetBean, dataMap));

        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

    /**
     * 取得要读取的key
     *
     * @return
     * @throws Exception
     */
    private Set<String> getOpcIdList() throws Exception {
        if (opcIdList.size() > 0) {
            //100s内不刷新
            if (System.currentTimeMillis() - lastTime < 100 * 1000) {
                return opcIdList;
            }
            opcIdList.clear();
            lastTime = System.currentTimeMillis();
        }
        Integer idx = Integer.valueOf(0);
        String identifier = opcTargetBean.getIdentifierPreview();
        try {
            OpcUaUtil.filter(identifier, opcIdList, WorkCache.getClient(opcTargetBean));
        } catch (Exception ex) {
            OpcUaUtil.filter(identifier, opcIdList, WorkCache.resetClient(opcTargetBean));
        }
        return opcIdList;
    }

    /**
     * 取得数据
     *
     * @return
     */
    private Map<String, Object> getOpcValue(List<String> tempList) {
        //读取
        List<DataValue> dataValues;
        try {
            dataValues = OpcUaUtil.readValues(tempList, WorkCache.getClient(opcTargetBean));
        } catch (Exception ex) {
            dataValues = OpcUaUtil.readValues(tempList, WorkCache.resetClient(opcTargetBean));
        }
        Map<String, Object> dataMap = new HashMap<>();
        if (dataValues == null || dataValues.size() == 0) {
            return dataMap;
        }
        String key;
        DataValue dv;
        for (int i = 0; i < tempList.size(); i++) {
            key = tempList.get(i);
            dv = dataValues.get(i);
            if (Objects.nonNull(dv) && Objects.nonNull(dv.getValue()) && Objects.nonNull(dv.getValue().getValue())) {
                dataMap.put(key, dv.getValue().getValue());
            }
        }
        return dataMap;
    }
}
